#include <iostream>

#include "city.hpp"
#include "foreach.hpp"
#include "formula.hpp"
#include "formula_callable.hpp"
#include "government.hpp"
#include "mapgen.hpp"
#include "merchant.hpp"
#include "unit.hpp"
#include "wml_node.hpp"
#include "world.hpp"

world::world(wml::const_node_ptr node)
  : map_(hex::generate_gamemap(node)),
    weather_x_(0), weather_y_(0),
    weather_next_turn_(game_logic::formula::create_optional_formula(node->attr("weather_next_turn"))),
    weather_function_(game_logic::formula::create_optional_formula(node->attr("weather_function")))
{
	sides_.push_back(government_ptr(new government));
}

void world::add_unit(const hex::location& loc, unit_ptr u)
{
	units_[loc].push_back(u);
	u->set_loc(loc);
}

void world::remove_unit(unit_ptr u)
{
	unit_map::iterator pos = units_.find(u->loc());
	assert(pos != units_.end());
	unit_list::iterator i = std::find(pos->second.begin(), pos->second.end(), u);
	assert(i != pos->second.end());
	pos->second.erase(i);
}

city_ptr world::add_city(wml::const_node_ptr node, const hex::location& loc, government_ptr gov)
{
	hex::tile_ptr t = map().get_tile(loc);
	city_ptr new_city(new city(node, *t, gov));
	t->set_city(new_city);
	cities_.push_back(new_city);
	return new_city;
}

int world::move_cost(const hex::location& loc, hex::DIRECTION dir) const
{
	if(!map().is_loc_on_map(loc)) {
		return -1;
	}

	const int road = map().get_tile(loc)->road(dir);
	if(road) {
		return 5;
	}

	hex::location dst = hex::tile_in_direction(loc, dir);
	if(!map().is_loc_on_map(dst)) {
		return -1;
	}

	return map().get_tile(dst)->move_cost();
}

void world::calculate_trades()
{
	foreach(city_ptr c, cities_) {
		estimate_merchant_profits(*this, c, 1000);
	}

	foreach(city_ptr c, cities_) {
		c->execute_trades(*this);
	}

	foreach(hex::tile_ptr t, map().tiles()) {
		t->clear_influences();
	}

	foreach(city_ptr c, cities_) {
		c->calculate_influence(*this);
	}
}

void world::pay_workers()
{
	foreach(city_ptr c, cities_) {
		c->pay_workers();
	}
}

void world::build_projects()
{
	foreach(city_ptr c, cities_) {
		c->work_on_projects(*this);
	}
}

const unit_list& world::units_on_tile(const hex::location& loc) const
{
	static const unit_list empty_list;
	unit_map::const_iterator i = units_.find(loc);
	if(i == units_.end()) {
		return empty_list;
	}

	return i->second;
}

void world::build_road(const hex::location& loc, hex::DIRECTION dir)
{
	hex::location dst = hex::tile_in_direction(loc, dir);
	if(!map().is_loc_on_map(loc) || !map().is_loc_on_map(dst)) {
		return;
	}
	hex::tile_ptr src_tile = map().get_tile(loc);
	hex::tile_ptr dst_tile = map().get_tile(dst);
	src_tile->build_road(dir);
	dst_tile->build_road(static_cast<hex::DIRECTION>((dir + 3)%6));
}

void world::weather_next_turn()
{
	std::cerr << "weather next turn...\n";
	if(!weather_next_turn_ || !weather_function_) {
		return;
	}

	game_logic::map_formula_callable callable;
	callable.add("x", variant(weather_x_));
	weather_x_ = weather_next_turn_->execute(callable).as_int();
	callable.add("x", variant(weather_x_));
	weather_y_ = weather_function_->execute(callable).as_int();
	std::cerr << "weather -> " << weather_x_ << "," << weather_y_ << "\n";
}
